#ifndef __M_SINK_H__
#define __M_SINK_H__

#include "util.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include <memory>
#include <mutex>
#include <fstream>
#include <iostream>
#include <cassert>
#include <sstream>

namespace maglog {

// 日志输出基类，定义了日志输出的接口
class LogSink {
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {}
        virtual ~LogSink() {}
        
        // 纯虚函数，子类需要实现日志输出的具体方法
        virtual void log(const char *data, size_t len) = 0;
};

// 标准输出日志类，继承自LogSink，负责将日志输出到控制台
class StdoutSink : public LogSink {
    public:
        using ptr = std::shared_ptr<StdoutSink>;
        StdoutSink() = default;

        // 实现日志输出，将日志内容输出到标准输出
        void log(const char *data, size_t len) override {
            std::cout.write(data, len);
        }
};

// 文件输出日志类，继承自LogSink，负责将日志输出到文件
class FileSink : public LogSink {
    public:
        using ptr = std::shared_ptr<FileSink>;

        // 构造函数，初始化文件输出流并打开日志文件
        FileSink(const std::string &filename) : _filename(filename) {
            util::file::create_directory(util::file::path(filename));
            _ofs.open(_filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }

        // 获取日志文件名
        const std::string &file() { return _filename; }

        // 实现日志输出，将日志内容写入文件
        void log(const char *data, size_t len) override {
            _ofs.write(data, len);
            if (!_ofs.good()) {
                std::cerr << "日志输出文件失败！\n";
            }
        }

    private:
        std::string _filename;   // 日志文件名
        std::ofstream _ofs;      // 文件输出流
};

// 滚动日志类，继承自LogSink，负责管理大小受限的滚动日志文件
class RollSink : public LogSink {
    public:
        using ptr = std::shared_ptr<RollSink>;

        // 构造函数，初始化日志文件基本名和最大文件大小
        RollSink(const std::string &basename, size_t max_fsize)
            : _basename(basename), _max_fsize(max_fsize), _cur_fsize(0) {
            util::file::create_directory(util::file::path(basename));
        }

        // 实现日志输出，按需创建新文件并写入日志
        void log(const char *data, size_t len) override {
            initLogFile();
            _ofs.write(data, len);
            if (!_ofs.good()) {
                std::cerr << "日志输出文件失败！\n";
            }
            _cur_fsize += len;
        }

    private:
        // 初始化日志文件，按需创建新文件
        void initLogFile() {
            if (!_ofs.is_open() || _cur_fsize >= _max_fsize) {
                _ofs.close();
                std::string name = createFilename();
                _ofs.open(name, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_fsize = 0;
            }
        }

        // 创建带时间戳的日志文件名
        std::string createFilename() {
            time_t t = time(NULL);
            struct tm lt;
            localtime_r(&t, &lt);
            std::stringstream ss;
            ss << _basename;
            ss << (lt.tm_year + 1900);
            ss << (lt.tm_mon + 1);
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << ".log";
            return ss.str();
        }

    private:
        std::string _basename;   // 日志文件基本名
        std::ofstream _ofs;      // 文件输出流
        size_t _max_fsize;       // 日志文件的最大大小
        size_t _cur_fsize;       // 当前日志文件的大小
};

// 日志输出工厂类，提供创建日志输出对象的静态方法
class SinkFactory {
    public:
        // 创建指定类型的日志输出对象
        template<typename SinkType, typename ...Args>
        static LogSink::ptr create(Args&&...args) {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
};

} // namespace maglog

#endif // __M_SINK_H__
